#include "pwref.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

// A more extended test of weakrefs

pw::event delsync;

class worker : public pw::reftarget, public pw::pwthread
{
public:
	worker() : pw::reftarget(this), pw::pwthread(true) /* detached */
	{ }

	~worker()
	{
		pw::reftarget::invalidate();
		fprintf(stderr, "Worker: destroying %p\n", this);
	}

	void *execute()
	{
		fprintf(stderr, "Worker: waiting %p\n", this);
		delsync.wait();
		return 0;
	}
};

class client1 : public pw::pwthread
{
public:
	client1(pw::weakref *ref) :
		ref_(ref)
	{ }
		
	void *execute()
	{
		fprintf(stderr, "client1: weak ref %p\n", ref_);
		for (int i = 0; i < 5; i++) {
			void *w = ref_->grab();
			fprintf(stderr, "client1: got %p\n", w);
			sleep(1);
			fprintf(stderr, "client1: release\n");
			ref_->release();
			sched_yield(); // Linux mutexes aren't honest...
		}
		delete ref_;
		return 0;
	}
protected:
	pw::weakref *ref_;
};

class client2 : public pw::pwthread
{
public:
	client2(pw::weakref *ref) :
		ref_(ref)
	{ }
		
	void *execute()
	{
		fprintf(stderr, "client2: weak ref %p\n", ref_);
		fprintf(stderr, "client2: waiting\n");
		delsync.wait();
		delete ref_;
		fprintf(stderr, "client2: deleted ref\n");
		return 0;
	}
protected:
	pw::weakref *ref_;
};

int main()
{
	worker *w = new worker;
	pw::weakref *ref = w->newweakref();

	client1 c1(ref->copy());
	client2 c2(ref->copy());

	w->start();
	c1.start();
	c2.start();
	delete ref;

	sleep(3);
	delsync.signal();

	fprintf(stderr, "joining threads\n");
	c1.join();
	c2.join();

	return 0;
}


/* Sample result:

The test output may vary depending on the scheduling order
but in general should be like:

Worker: waiting 0xf5f010
client1: weak ref 0xf5f230
client1: got 0xf5f010
client2: weak ref 0xf5f350
client2: waiting
client1: release
client1: got 0xf5f010
client1: release
client1: got 0xf5f010
client1: release
client1: got 0xf5f010
joining threads
client2: deleted ref
client1: release
Worker: destroying 0xf5f010
client1: got (nil)
client1: release

*/
